#region Copyright Vanderlande Industries B.V. 2010-2012
//
// Copyright (c) 2010-2012 Vanderlande Industries.
// All rights reserved.
//
// @file:     Fsc.cs
// @version:  %version: 76 %
// %date_created: dinsdag 19 februari 2013 18:41:15 %
//
// The copyright to the computer program(s) herein is the property of
// Vanderlande Industries. The program(s) may be used and/or copied
// only with the written permission of the owner or in accordance with
// the terms and conditions stipulated in the contract under which the
// program(s) have been supplied.
//
#endregion

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.ServiceProcess;
using System.Threading;
using System.Windows.Forms;
using VI.Fsc.Common.FileTransfer;
using VI.Fsc.Common.FileTransfer.Model;
using VI.Fsc.Common.Util;
using VI.Fsc.OperatorGui.WebReferences.Opc;
using VI.Fsc.OperatorGui.Models.Application;
using VI.Fsc.OperatorGui.Models.Application.Util;
using VI.Fsc.OperatorGui.Models.Fsc.Configuration;
using VI.Fsc.OperatorGui.Models.Subscription;
using System.IO;
using System.Xml;

namespace VI.Fsc.OperatorGui.Models.Fsc
{
    // Handler for the update on the new components error list.
    public delegate void NewErrorsEventHandler(List<CStructureItem> errorsList);
    // Handler for the speed change event.
    public delegate void SetSpeedEventHandler(int speed);

    public delegate void NewModeEventHandler(string mode);

    public delegate void FscStartupProgressUpdateEventHandler();

    public delegate void FscStartupFailedEventHandler();

    public delegate void FscVersionInfoEventHandler(string versioninfo);

    /// <summary>
    /// Class to interface with the FSC-OPC-Server.
    /// </summary>
    public class CFsc
    {
        #region members

        private AsyncOperation asyncOperation;

        public event FscErrorEventHandler FscErrorEvent;
        public event NewErrorsEventHandler NewErrorsEvent;
        public event SetSpeedEventHandler SetSpeedEvent;
        public event NewModeEventHandler NewModeEvent;
        public event FscStartupProgressUpdateEventHandler FscStartupProgressUpdateEvent;
        public event FscStartupFailedEventHandler FscStartupFailedEvent;
        public event FscVersionInfoEventHandler FscVersionInfoEvent;

        protected const string FSCSpeedsPath = "System/Settings/Speed";
        protected const string COMMAND_RESET = "/Commands/Reset";
        private const string COMMAND_AREA_MODE = "/Commands/SetMode";

        private List<string> subscriptionIds;

        private bool statusRunning;

        private int speed;

        private SorterMode mode;

        #endregion members

        #region properties

        /// <summary>
        /// Gets or sets whether the FSC is started (true) or stopped (false).
        /// </summary>
        public bool StatusRunning
        {
            get
            {
                return statusRunning;
            }
            set
            {
                statusRunning = value;
            }
        }

        /// <summary>
        /// Gets or sets the current speed of the fsc.
        /// </summary> 
        public int Speed
        {
            get
            {
                return this.speed;
            }
            private set
            {
                speed = value;

                if (null != SetSpeedEvent)
                {
                    SetSpeedEvent(speed);
                }
            }
        }

        /// <summary>
        /// Gets or sets the mode of the FSC server, as transmitted by it.
        /// </summary>
        public SorterMode Mode
        {
            get
            {
                return mode;
            }
            set
            {
                mode = value;
            }
        }

        #endregion properties

        /// <summary>
        /// Creates a new instance of the fsc class.
        /// </summary>
        public CFsc()
        {
            //Logger.Entering(MethodBase.GetCurrentMethod());

            this.asyncOperation = AsyncOperationManager.CreateOperation(this);
            XmlDocument section = new XmlDocument();
            section.Load("Controllers/Configuration/Vision.exe.config");
            XmlElement node = section.DocumentElement;
            if (node["fscconfig"] == null)
            {
                throw new CFscException(
                    MethodBase.GetCurrentMethod(),
                    "Invalid or missing application configuration section",
                    "InvalidApplicationConfiguration");
            }

            // As precaution assume that the system is running per default.
            this.statusRunning = true;

            subscriptionIds = new List<string>();

            // When initializing the object, we do not set any default speed, we let
            // the speed value to be set once the OPC information is retrieved.

            //Logger.Exiting(MethodBase.GetCurrentMethod());
        }

        /// <summary>
        /// Writes start command to fsc.
        /// </summary>
        public void Start()
        {
            CFscUtility.SetItem(CFscConfiguration.Instance.StartCommandPath, true);
        }

        /// <summary>
        /// Writes stop command to fsc.
        /// </summary>
        public void Stop()
        {
            CFscUtility.SetItem(CFscConfiguration.Instance.StopCommandPath, true);
        }

        /// <summary>
        /// Writes reset command to fsc. If we are in maintenance mode, we try to send reset commands to the MAO and to the area,
        /// otherwise we will send a reset to the area only
        /// </summary>
        public void Reset()
        {
            Dictionary<string, object> commands = new Dictionary<string, object>();

            if (Mode == SorterMode.Maintenance)
            {
                string maoResetPath = GetMaoPath() + COMMAND_RESET;

                if (CFscUtility.ReadItem(maoResetPath) != null)
                {
                    commands.Add(maoResetPath, DateTime.Now.ToString());
                }
                else
                {
                    //Logger.Warn(
                    //    MethodBase.GetCurrentMethod(),
                    //    "Could not do a reset on the Maintenance area object, because the reset command does not exist");
                }
            }

            // Add  the normal reset too
            commands.Add(CFscConfiguration.Instance.ResetCommandPath, true);
            
            // Send the commands
            CFscUtility.SetItems(commands);
        }

        /// <summary>
        /// Writes new speed value to fsc.
        /// </summary>
        /// <param name="newSpeed">new speed of the fsc</param>
        public void SetSpeed(int newSpeed)
        {
            this.Speed = newSpeed;
            CFscUtility.SetItem(CFscConfiguration.Instance.SetSpeedCommandPath, newSpeed);
        }

        /// <summary>
        /// Writes new speed value to fsc.
        /// </summary>
        /// <param name="newSpeed">new speed of the fsc</param>
        public void SetMode(int mode)
        {
            CFscUtility.SetItem(GetModePath(), mode);
        }

        /// <summary>
        /// Searches the OPC tree for maintenance objects to answer the question 
        /// if maintenance mode is available.
        /// </summary>
        /// <returns>true, if maintenance available, otherwise false</returns>
        public bool IsMaintenanceAvailable()
        {
            return !string.IsNullOrEmpty(GetMaoPath());
        }

        /// <summary>
        /// Get all elements from the OPC tree that own the given markcode.
        /// </summary>
        /// <param name="markcode">The markcode to look for.</param>
        /// <returns>List of elements with given markcode</returns>
        public Dictionary<string, CMaintenanceDisplayItem> GetItemsByMarkcode(string markcode)
        {
            return CFscConfiguration.Instance.GetStatusItemsByMarkcode(markcode);
        }

        /// <summary>
        /// Get all elements from the OPC tree that own the given markcode.
        /// </summary>
        /// <param name="markcode">The markcode to look for.</param>
        /// <returns>List of elements with given markcode</returns>
        public CStructureItem GetItemsByMarkcodeAndFunctionVariant(string markcode, string functionVariant)
        {
            return CFscConfiguration.Instance.GetItemsByMarkcodeAndFunctionVariant(markcode, functionVariant);
        }

        /// <summary>
        /// Get all elements from the OPC tree that own the given shortId.
        /// </summary>
        /// <param name="markcode">The shortId to look for.</param>
        /// <returns>The element with given shortId</returns>
        public CStructureItem GetItemByShortId(string shortId)
        {
            return CFscConfiguration.Instance.GetItemByShortId(shortId);
        }

        /// <summary>
        /// Gets the path to the (first) maintenance object.
        /// </summary>
        /// <remarks>Returns an empty string, if no object was found</remarks>
        /// <returns>The path to the maintenance object.</returns>
        protected string GetMaoPath()
        {
            string retVal = string.Empty;

            List<CStructureItem> maos = CFscConfiguration.Instance.GetItemsByMarkcode(CFscStrings.MARKCODE_MAINTENANCEOBJECT_STRING);
            if (maos.Count > 0)
            {
                CStructureItem mao = maos[0];
                retVal = mao.Id;
            }

            return retVal;
        }

        /// <summary>
        /// Gets the path to the sorter section. Precondition is that this is the parent of the Mao.
        /// </summary>
        /// <remarks>Returns an empty string, if no object was found</remarks>
        /// <returns>The path to the maintenance object.</returns>
        public string GetSorterSectionPath()
        {
            string retVal = string.Empty;

            List<CStructureItem> maos = CFscConfiguration.Instance.GetItemsByMarkcode(CFscStrings.MARKCODE_MAINTENANCEOBJECT_STRING);
            
            if (maos.Count == 1)
            {
                CStructureItem mao = maos[0];
                retVal = mao.ParentId;
            }

            return retVal;
        }

        /// <summary>
        /// Gets the path for the SetMode command.
        /// </summary>
        /// <remarks>Discussed with Tim. We search and set the mode of the sorter section, so the parent of the mao</remarks>
        /// <returns>The path of the SetMode-command, empty string, if not found.</returns>
        private string GetModePath()
        {
            //Logger.Entering(MethodBase.GetCurrentMethod());

            string retVal = string.Empty;

            string sorterSectionId = GetSorterSectionPath();

            if (!string.IsNullOrEmpty(sorterSectionId))
            {
                retVal = sorterSectionId + CFscStrings.COMMAND_SETMODE;
            }
            else
            {
                //Logger.Error(MethodBase.GetCurrentMethod(), "There is no SetMode on the sorter section");
            }

           // Logger.Exiting(MethodBase.GetCurrentMethod(), "retVal = " + retVal);
            return retVal;
        }

        /// <summary>
        /// Reads a speed item of the fsc from the OPC tree.
        /// The item to read is specified by the provided path.
        /// </summary>
        /// <param name="itemPath">The path of the item to read.</param>
        private int ReadSpeedItem(string itemPath)
        {
            int speedValue = 0;

            try
            {
                ItemValue speedItem = CFscUtility.ReadItem(itemPath);

                if (speedItem != null)
                {
                    speedValue = Convert.ToInt32(speedItem.Value);
                }
                else
                {
                    throw new Exception("Speed does not exist - [" + itemPath + "]");
                }
            }
            catch (CFscException fscEx)
            {
                throw fscEx;
            }
            catch (Exception e)
            {
                throw new CFscException(MethodBase.GetCurrentMethod(), e.Message, "ReadSpeedItem");
            }

            return speedValue;
        }

        /// <summary>
        /// Try to read the FscStartup path from the opcserver and store it in the application settings.
		/// The path is still kept in application settings for now because parts of the software rely on it.
        /// </summary>
        /// <returns>true if the item was read and stored successfully, false otherwise</returns>
        private bool ReadFscPathFromOpcServer()
        {
            string itemValue = GetStringValueFromFscItem(CFscStrings.FSC_STARTUPPATH);
            bool readSuccessful = !String.IsNullOrEmpty(itemValue);
            if (readSuccessful)
            {
                OperatorGuiApplicationSettings.Instance.FscConfigPath = itemValue;
            }
            return readSuccessful;
        }

        /// <summary>
        /// Reads a speed item of the fsc from the OPC tree.
        /// The item to read is specified by the provided path.
        /// </summary>
        /// <param name="itemPath">The path of the item to read.</param>
        public int GetMode()
        {
            int modeValue = 0;
            try
            {
                ItemValue modeItem = CFscUtility.ReadItem(GetModePath());

                if (modeItem != null)
                {
                    modeValue = Convert.ToInt32(modeItem.Value);
                }
            }
            catch (Exception e)
            {
                throw new CFscException(MethodBase.GetCurrentMethod(), e.Message, "GetMode");
            }

            return modeValue;
        }

        /// <summary>
        /// Get a string value from the fsc item at location path
        /// </summary>
        /// <param name="path"></param>
        /// <param name="postfix"></param>
        /// <returns></returns>
        protected string GetStringValueFromFscItem(string path)
        {
            string retVal = string.Empty;

            try
            {
                ItemValue itemValue = CFscUtility.ReadItem(path);
                retVal = itemValue.Value.ToString();
            }
            catch (Exception)
            {
                //Logger.Error(MethodBase.GetCurrentMethod(), string.Format("Could not get string value for item '{0}'", path));
            }

            return retVal;
        }

        /// <summary>
        /// Loads the available speeds from the OPC tree. We don't know how many speeds are
        /// there, so, we start requesting one by one. Once a requested tag does not exist,
        /// we exit the loop. At the end the current set speed is read as well.
        /// </summary>
        private void LoadAvailableSpeeds()
        {
            bool isItemOk = true;
            int counter = 1;

           
            // Loop to find all available speed values.
            while (isItemOk)
            {
                try
                {
                    int speed = ReadSpeedItem(FSCSpeedsPath + counter);
                    CFscConfiguration.Instance.PossibleSpeedValues.Add(speed);
                    //Logger.InfoFormat(MethodBase.GetCurrentMethod(), "Speed {0} for {1}",
                    //   speed, FSCSpeedsPath + counter);

                    counter++;
                }
                catch (Exception e)
                {
                    //Logger.Error(MethodBase.GetCurrentMethod(), e);
                    isItemOk = false;
                }
            }

            // Sort the speeds by value.
            CFscConfiguration.Instance.PossibleSpeedValues.Sort();

            try
            {
                // Read the current set speed.
                try
                {
                    Speed = ReadSpeedItem(CFscConfiguration.Instance.SetSpeedCommandPath);
                }
                catch (Exception e)
                {
                    //Logger.Error(MethodBase.GetCurrentMethod(), e.Message);
                    Speed = 0;
                }


                // In case the possible speeds collection is empty, add the value from the previous step.
                if (CFscConfiguration.Instance.PossibleSpeedValues.Count == 0)
                {
                    CFscConfiguration.Instance.PossibleSpeedValues.Add(Speed);
                }
            }
            catch (Exception e)
            {
                //Logger.Error(MethodBase.GetCurrentMethod(), e.Message);
            }
        }

        /// <summary>
        /// Handles the reconnection to the server.
        /// </summary>
        /// <param name="isOnlineStatus">Indicates the online status of the application.</param>
        public void HandleConnectionStatusChanged(bool isOnlineStatus)
        {
            //Logger.Entering(MethodBase.GetCurrentMethod(), "The online status is " + isOnlineStatus);

            // If we are online, we load the configuration.
            if (isOnlineStatus)
            {
                // Read FSC path from the OPC server and if successful, start downloading the files from this path.
                if (ReadFscPathFromOpcServer())
                {
                    LoadFilesByFtpConnection();
                }
                else
                {
                    //Logger.Warn(MethodBase.GetCurrentMethod(), 
                    //    "Read fsc path from opc failed. No files can be downloaded, user is notified.");
                    if (FscStartupFailedEvent != null)
                    {
                        FscStartupFailedEvent();
                    }
                }
                // First request the available speeds.
                LoadAvailableSpeeds();
            }
            else
            {
                // If we are offline, we stop any activity we have with the opcServer.
                RestartServerActivity();
            }

            // Start/Stop the request of the update of the controller time.
            CFscDateTime.Instance.ToggleUpdateFscDateTime(isOnlineStatus);

            //Logger.Exiting(MethodBase.GetCurrentMethod());
        }

        /// <summary>
        /// The client must download some files that are generated from databases on the server at start-up.
        /// </summary>
        
        public virtual void LoadFilesByFtpConnection()
        {
            //Logger.Entering(MethodBase.GetCurrentMethod());

            ClearDownloadDirectory(CFileConstants.OGuiLocalDiagnosticsFilesPath);

            string applicationName = CFscUtility.GetApplicationName();

            FileTransfer.Instance.StartDownloadMultipleFiles(
                        OperatorGuiApplicationSettings.Instance.FscIpAddress,
                        OperatorGuiApplicationSettings.Instance.FscConfigPath,
                        CFileConstants.OGuiLocalDiagnosticsFilesPath,
                        CFileConstants.DiagnosticsFilesDownloadList,
                        OnDownloadDiagnosticsFilesFinished);

            //Logger.Exiting(MethodBase.GetCurrentMethod());
        }
        
        
        public virtual void OnDownloadDiagnosticsFilesFinished(FtpActionResultState ftpActionResult)
        {
            //Logger.Entering(MethodBase.GetCurrentMethod());
            if (ftpActionResult.HasError)
            {
               // Logger.Error(MethodBase.GetCurrentMethod(), "Exception occurred on ftpAction", ftpActionResult.Error);
                // If downloading the diagnostics files failed, then the xml file that contains the fsc structure 
                // is not available. The read fsc structure from xml will not be started, so we should start reading 
                // the fsc structure from OPC.
                CFscStructureReader.Instance.ReadRemoteFscStructure(true);
            }
            else
            {
                // Downloading of files from the fsc path has finished. Start downloading the files from /etc.
                // e.g. Dhcp en host file
                NotifyFscStartupProgress();
                DownloadNetworkConfigFiles();
            }
            //Logger.Exiting(MethodBase.GetCurrentMethod());
        }
       
       
        /// <summary>
        /// The client must download some network config files to get e.g. ip addresses of embedded controllers.
        /// </summary>
        public virtual void DownloadNetworkConfigFiles()
        {
           // Logger.Entering(MethodBase.GetCurrentMethod());

            string applicationName = CFscUtility.GetApplicationName();

            FileTransfer.Instance.StartDownloadMultipleFiles(
                        OperatorGuiApplicationSettings.Instance.FscIpAddress,
                        "/etc",
                        CFileConstants.OGuiLocalDiagnosticsFilesPath,
                        CFileConstants.EmbeddedControllerFilesDownloadList,
                        OnDownloadNetworkConfigFilesFinished);

           // Logger.Exiting(MethodBase.GetCurrentMethod());
        }
      
        private void ClearDownloadDirectory(string directory)
        {
            //Logger.Entering(MethodBase.GetCurrentMethod());

            // If the directory for temporary placement of downloaded files already exists, clear it.
            if (Directory.Exists(directory))
            {
                foreach (string file in Directory.GetFiles(directory))
                {
                    try
                    {
                        // Remove read only flag if nescessary
                        FileInfo myFile = new FileInfo(file);
                        if (myFile != null && myFile.IsReadOnly)
                        {
                            myFile.Attributes -= FileAttributes.ReadOnly;
                        }

                        // One may think that one must prefix the file with the directory but GetFiles
                        // "Returns the names of files (including their paths) in the specified directory.".
                        File.Delete(file);
                    }
                    catch (IOException e)
                    {
                        //Logger.Warn(MethodBase.GetCurrentMethod(), "Problem when deleting file " + file + ". " + e);
                    }
                }
            }
            //Logger.Exiting(MethodBase.GetCurrentMethod());
        }

        /// <summary>
        /// Check the version.
        /// </summary>
        /// <returns>true, if the version from the local file and the file downloaded from the server match
        /// or if the user has opted for continuing in spite of unmatching versions.</returns>
        protected virtual void CheckVersionInformation()
        {
            //Logger.Entering(MethodBase.GetCurrentMethod());

            bool versionsMatch = false;
            // If there is no local version file the server is offline, continue, do not show any message.
            bool isOnline = COPCServerConnectionService.Instance.IsOnline;
            if (!CVersion.IsLocalGuiVersionAvailable() || !COPCServerConnectionService.Instance.IsOnline)
            {
                //Logger.Info(MethodBase.GetCurrentMethod(), "Comparing of versions not possible. Server online: " + isOnline +
                   // ". Application continuing.");
            }
            else
            {
                
                // If the GUI version from the server could not be downloaded, show a message which says so.
                // Also ask whether the user wants to continue with the GUI.
                if (!CVersion.IsGuiVersionFromServerAvailable(CFileConstants.OGuiLocalDiagnosticsFilesPath))
                {
                    //GUI Version cannot be retrieved!

                    // The following line ensures that the dialog is closed immediately after confirmation.
                    if (FscVersionInfoEvent != null)
                    {
                        FscVersionInfoEvent("NoServerGuiVersionHeader");
                    }

                }
                else
	            {
                    versionsMatch = CVersion.DoVersionsMatch(CFileConstants.OGuiLocalDiagnosticsFilesPath);
                    // The version files are both present but do not match: Show a message that the versions
                    // do not match and ask whether the user wants to continue.
                    string versionsMatchMessage = versionsMatch ? string.Empty : "GuiVersionMismatchHeader";
                    if (!versionsMatch)
                    {
                        if (FscVersionInfoEvent != null)
                        {
                            FscVersionInfoEvent(versionsMatchMessage);
                        }
                    }
                }
            }

            //Logger.Exiting(MethodBase.GetCurrentMethod(), "Version is deemed ok = " +
                //versionsMatch);
        }

        /// <summary>
        /// Callback of the download files by ftp. 
        /// </summary>
        /// <param name="ftpActionResult">Result of the FtpAction</param>
        private void OnDownloadNetworkConfigFilesFinished(FtpActionResultState ftpActionResult)
        {
            //Logger.Entering(MethodBase.GetCurrentMethod());

            if (ftpActionResult.HasError)
            {
                //Logger.Error(MethodBase.GetCurrentMethod(), "Exception occurred on ftpAction", ftpActionResult.Error);
                // If downloading the diagnostics files failed, then the xml file that contains the fsc structure 
                // is not available. The read fsc structure from xml will not be started, so we should start reading 
                // the fsc structure from OPC.
                CFscStructureReader.Instance.ReadRemoteFscStructure(true);
            }
            else
            {
                NotifyFscStartupProgress();
                // Do refreshing of texts in the main thread (GUI thread) to block the GUI.
                this.asyncOperation.Post(ContinueMainThreadAfterDownloadCallback, null);
            }

            //Logger.Exiting(MethodBase.GetCurrentMethod());
        }

        private void ContinueMainThreadAfterDownloadCallback(object o)
        {
            //Logger.Entering(MethodBase.GetCurrentMethod());

            CheckVersionInformation();
                CopyFromTempToWorkingDirectory();
                // All files are downloaded, we can start loading the fsc configuration. This is done in a 
                // new thread, so that it does not block the UI.
                // This can only be started now because this method may need the downloaded opcitems.xml file.
                NotifyFscStartupProgress();
                CFscStructureReader.Instance.ReadRemoteFscStructure();

                //Logger.Info(MethodBase.GetCurrentMethod(), "All files downloaded, refresh text cache, reload texts.");
                //VIForm.TextSession.RefreshTextCache();
               // VIForm.ReloadTexts();

			
			//Logger.Exiting(MethodBase.GetCurrentMethod());
        }

        /// <summary>
        /// Copies all files from the default download directory to the working directory except the GUI-version file.
        /// </summary>
        public virtual void CopyFromTempToWorkingDirectory()
        {
            //Logger.Entering(MethodBase.GetCurrentMethod());

            if (Directory.Exists(CFileConstants.OGuiLocalDiagnosticsFilesPath))
            {
                bool copyFailed = false;

                // Copy downloaded files from temporary directory to working directory.
                foreach (string fileName in Directory.GetFiles(CFileConstants.OGuiLocalDiagnosticsFilesPath, "*.xml"))
                {
                    // Documentation of Directory.GetFiles:
                    // "Returns the names of files (including their paths) in the specified directory.".
                    // Thus, for correct processing, we need to strip the path first.
                    string fileNameWithoutPath = Path.GetFileName(fileName);
                    if (!fileNameWithoutPath.Equals(CFileConstants.GuiVersionFile))
                    {
                        try
                        {
                            //Logger.Debug(MethodBase.GetCurrentMethod(), "Copying " + fileName + " to the working directory.");
                            // Documentation of File.Copy: "The sourceFileName and destFileName parameters can specify
                            // relative or absolute path information. Relative path information is interpreted
                            // as relative to the current working directory."
                            // Thus, the following will copy $temp\file to .\file, which is what we want.
                            File.Copy(fileName, fileNameWithoutPath, true);
                        }
                        catch (IOException exception)
                        {
                            //Logger.Error(MethodBase.GetCurrentMethod(), "Could not copy file to the working directory", exception);
                            copyFailed = true;

                        }
                    }
                }

                if (copyFailed)
                {
                    //Logger.Info(MethodBase.GetCurrentMethod(), "Error VIDialog opened");

                    //VIDialog.ShowErrorDialog("FSC", "CannotCopyFilesTitle", "CannotCopyFilesMessage");

                    //Logger.Info(MethodBase.GetCurrentMethod(), "Error VIDialog closed");
                }
            }
            else
            {
                //Logger.Error(
                    //MethodBase.GetCurrentMethod(), 
                    //"Could not copy files from directory \"" + CFileConstants.OGuiLocalDiagnosticsFilesPath + "\" because it does not exists");
            }

            //Logger.Exiting(MethodBase.GetCurrentMethod());
        }

        /// <summary>
        /// Stops the activity with the server: the connection with the OPC and the SOAP servers will be stopped,
        /// gracefully if possible.
        /// </summary>
        /// <param name="notifyWhenStopped">If requesting is stopped, this parameter has the following meaning:
        /// true: An event notifying about this change of the subscription is raised.</br>
        /// false: No event is raised.</br>
        /// If requesting is started, this parameter has no relevance.</param>
        public void StopServerActivity(bool notifyWhenStopped)
        {
            //Logger.Entering(MethodBase.GetCurrentMethod());

            // Stop any configuration read that may be taking place.
            CFscStructureReader.Instance.StopServerActivity();

            // Stop any subscription polling that may be taking place.
            foreach (string subscriptionId in subscriptionIds)
            {
                CSubscriptionManager.Instance.StopSubscription(subscriptionId, notifyWhenStopped);
            }
            subscriptionIds.Clear();

            COPCServerConnectionService.Instance.StopConnection();
            CSoapServerConnectionService.Instance.StopConnection();

            //Logger.Exiting(MethodBase.GetCurrentMethod());
        }

        public void RestartServerActivity()
        {
            //Logger.Entering(MethodBase.GetCurrentMethod());

            //Logger.Info(MethodBase.GetCurrentMethod(), "Will stop and start.");
            StopServerActivity(true);

            // Clear the configuration to be sure that the old configuration is not used anymore.
            CFscConfiguration.Instance.ClearConfiguration();

            //- Reconnect to FSC and retrieve config
            // Restart the connection.
            COPCServerConnectionService.Instance.Restart();
            CSoapServerConnectionService.Instance.Restart();

            //Logger.Exiting(MethodBase.GetCurrentMethod());
        }

        /// <summary>
        /// Performs the actions needed to do after the FSC configuration load is completed:
        ///     start the subscription on all FSC components
        /// </summary>
        public void FinishReadRemoteFscConfiguration()
        {
            // Starts the subscription mechanism
            StartSubscription();
            
        }

        /// <summary>
        /// Starts the subscription of all the fsc components. Starting the subscription
        /// will associate a method that will be called once there are changes 
        /// in the subscribed items.
        /// </summary>
        private void StartSubscription()
        {
            string subscriptionId = CSubscriptionManager.Instance.StartNewSubscription(
                COPCServerConnectionService.Instance.OPCServer,
                (short)CFscConfiguration.Instance.GetOpcWebServiceSettings().PingRate,
                CFscConfiguration.Instance.GetSubscribeRequestItemList(),
                HandleChangesInSubscribedItems,
                HandlePollException);

            if (!string.IsNullOrEmpty(subscriptionId))
            {
                subscriptionIds.Add(subscriptionId);
            }
        }

        /// <summary>
        /// Fires an event, notifying about an error.
        /// </summary>
        /// <param name="isFatalError">Indicates whether the error is considered fatal.</param>
        /// <param name="context">The context for the message.</param>
        /// <param name="title">The key of the title of the message dialog.</param>
        /// <param name="messageTextKey">The key of the message. If null or empty no message will be displayed.</param>
        private void HandleError(bool isFatal, string context, string title, string messageTextKey)
        {
            if (null != FscErrorEvent)
            {
                FscErrorEvent(isFatal, context, title, messageTextKey);
            }
        }

        /// <summary>
        /// Fires an event, notifying about the new components that are in error state.
        /// </summary>
        protected void NotifyAboutErrors()
        {
            if (null != NewErrorsEvent)
            {
                NewErrorsEvent(CFscConfiguration.Instance.GetOrderedErrorsAndWarnings());
            }
        }

        /// <summary>
        /// Fires an event, notifying about the new carrier at the maintenance position.
        /// </summary>
        protected void NotifyNewMode(string mode)
        {
            if (null != NewErrorsEvent)
            {
                NewModeEvent(mode);
            }
        }

        /// <summary>
        /// Fires an event, notifying about progress update during fsc startup and initialization.
        /// </summary>
        protected void NotifyFscStartupProgress()
        {
            if (null != FscStartupProgressUpdateEvent)
            {
                FscStartupProgressUpdateEvent();
            }
        }

        private void HandlePollException(Exception e)
        {
            CFscException fscEx = new CFscException(MethodBase.GetCurrentMethod(), e.ToString(), "ConnexionLost");
            //Logger.Error(MethodBase.GetCurrentMethod(), fscEx.Message);
        }

        private void HandleChangesInSubscribedItems(SubscribePolledRefreshReplyItemList[] listOfChanges)
        {
            try
            {
                if ((listOfChanges != null) && (listOfChanges.Length > 0))
                {
                    // We get the list of changes and update the data structure in 
                    // memory. Nothing else is needed to be done!
                    foreach (SubscribePolledRefreshReplyItemList itemList in listOfChanges)
                    {
                        foreach (ItemValue value in itemList.Items)
                        {
                            UpdateComponentStatus(value);
                        }
                    }

                    // We fire the event with the new list of errors. The controller
                    // will then get the list of errors and ask the view to display it.
                    NotifyAboutErrors();

                    // Now that all changes and updates have been made, we can check for relevant things 
                    // where we have to notify listeners.
                    CheckAndNotifyAboutChanges(listOfChanges);
                }
            }
            catch (CFscInvalidStateException fscEx)
            {
                // An unknown state was found, so, we need to reload the configuration.
                //Logger.Error(fscEx.Method, "Invalid state encountered in one of the FSC items.");
                COPCServerConnectionService.Instance.IsOnline = false;
            }
            catch (Exception ex)
            {
                // An unknown state was found, so, we need to reload the configuration.
                //Logger.Error(MethodBase.GetCurrentMethod(), ex.Message);
            }
        }

        /// <summary>
        /// Updates the status of an fsc component.
        /// </summary>
        /// <param name="item">The item to be updated, containing the new values as well.</param>
        private void UpdateComponentStatus(ItemValue opcItem)
        {
            string fullPath = opcItem.ItemPath + opcItem.ItemName;

            try
            {
                CStructureItem structureItem = CFscConfiguration.Instance.StructureTree[fullPath];

                structureItem.Value = opcItem.Value.ToString();
                structureItem.Timestamp = opcItem.FscDateTime;
                //Logger.Debug(MethodBase.GetCurrentMethod(), "Updating status for " + fullPath + " to " + structureItem.Value);
            }
            catch (Exception e)
            {
                //Logger.Error(MethodBase.GetCurrentMethod(), e.Message);
            }
        }

        /// <summary>
        /// Checks the list and notifies listeners about relevant changes.
        /// </summary>
        /// <param name="item">The item, containing the new values as well.</param>
        protected virtual void CheckAndNotifyAboutChanges(SubscribePolledRefreshReplyItemList[] listOfChanges)
        {
            // do a search for the sorter mode path. This will iterate trough the complete configuration,
            // so only do this once and not in the foreach loop
            string sorterModePath = CFscConfiguration.Instance.GetSorterModePath();

            // If there is no MAO object the sorterModePath is empty, which makes this method useless
            if (!string.IsNullOrEmpty(sorterModePath))
            {
                foreach (SubscribePolledRefreshReplyItemList itemList in listOfChanges)
                {
                    foreach (ItemValue item in itemList.Items)
                    {
                        try
                        {
                            string fullPath = item.ItemPath + item.ItemName;

                            if (fullPath == sorterModePath)
                            {
                                //Logger.Debug(MethodBase.GetCurrentMethod(), "Change in item = " + fullPath + ", Value = " + item.Value);
                                NotifyNewMode(item.Value.ToString());
                            }
                        }
                        catch (Exception e)
                        {
                            //Logger.Error(MethodBase.GetCurrentMethod(), e.Message);
                        }
                    }
                }
            }
        }

        public bool IsInMaintenanceMode()
        {
            return mode == SorterMode.Maintenance;
        }

        public void SetAreaMode(SorterMode sorterMode)
        {
            var cStructureItem = CFscConfiguration.Instance.GetArea();
            var path = cStructureItem.Key + COMMAND_AREA_MODE;
            CFscUtility.SetItem(path, (int) sorterMode);
        }
    }
}